package com.google.android.apps.skipbot;

import java.io.File;
import java.util.ArrayList;

import roboguice.inject.InjectView;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.DialogInterface.OnShowListener;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.TypedValue;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsoluteLayout;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.Spinner;
import android.widget.TextView;

@SuppressWarnings("deprecation")
public class PoseCreatorActivity extends SkipperActivity {
  protected static final int DIALOG_SAVE_ID = 1;
  private static final int DIALOG_LOAD_ID = 2;
  static final String CALIBRATION_POSE_PREFIX = "CALIBRATION";
  @InjectView(R.id.adjustment)
  SeekBar adjustmentBar;
  @InjectView(R.id.increment)
  Button incrementButton;
  @InjectView(R.id.decrement)
  Button decrementButton;
  @InjectView(R.id.attach)
  Button attachButton;
  @InjectView(R.id.attach_all)
  Button attachServosButton;
  @InjectView(R.id.save)
  Button saveButton;
  @InjectView(R.id.load)
  Spinner loadSpinner;
  @InjectView(R.id.detach_all)
  Button detachServosButton;

  @InjectView(R.id.pose_canvas)
  ChangableAbsoluteLayout poseCanvas;

  @InjectView(R.id.position_controls)
  View positionControls;
  @InjectView(R.id.control_bar)
  View controlBar;

  private ArrayList<ServoAdjustor> servos;
  protected View lastSelected;
  protected int lastServoIndex;
  private Dialog dialog;
  private ArrayList<Button> buttons;
  private String lastLoadedOrSavedPose = null;
  String calibrationPoseName;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.pose_creator);

    if (servos == null) {
      servos = new ArrayList<ServoAdjustor>();
    }

    saveButton.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        showDialog(DIALOG_SAVE_ID);
      }
    });

    detachServosButton.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        if (robotService.getRobot() != null) {
          robotService.getRobot().detachAllServos();
          updateButtons();
        }
      }
    });

    attachServosButton.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        robotService.getRobot().attachAllServos();
        robotService.getRobot().refreshAllServos();
        updateButtons();
      }
    });

    poseCanvas.setOnResizeListener(new OnResizeListener() {
      @Override
      public void OnResize(int id, int xNew, int yNew, int xOld, int yOld) {
        if (xNew != xOld && yNew != yOld) {
          addButtons();
          updateButtons();
        }
      }
    });

    SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
    String currentConfig = sharedPreferences.getString(SkipperPreferences.OPT_ROBOT_CONFIG,
        SkipperPreferences.SKIPPER_LARGE);
    calibrationPoseName = CALIBRATION_POSE_PREFIX + '_' + currentConfig;

    refreshPoseList();
  }

  protected void refreshPoseList() {
    String[] list = getPoseList();
    int selectedIndex = 0;
    for (int i = 0; i < list.length; ++i) {
      if (list[i].equals(lastLoadedOrSavedPose)) {
        selectedIndex = i;
      }
    }

    final ArrayAdapter<String> poseList = new ArrayAdapter<String>(this,
        android.R.layout.simple_spinner_item, list);
    poseList.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    loadSpinner.setAdapter(poseList);
    loadSpinner.setSelected(false);
    loadSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
      @Override
      public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        loadPose(poseList.getItem(position));
        updateButtons();
      }

      @Override
      public void onNothingSelected(AdapterView<?> arg0) {
      }
    });

    loadSpinner.setSelection(selectedIndex, true);
  }

  /**
   * Gets the saved poses as well as any calibration poses.
   *
   * @return a list of saved poses and calibration poses
   */
  protected String[] getPoseList() {
    File directory = getFilesDir();
    String[] list = directory.list();

    boolean hasCalibration = false;

    for (String poseName : list) {
      if (poseName.equals(calibrationPoseName)) {
        hasCalibration = true;
        break;
      }
    }

    String[] outputList;

    if (!hasCalibration) {
      outputList = new String[list.length + 1];
      outputList[0] = calibrationPoseName;
      for (int i = 0; i < list.length; ++i) {
        outputList[i + 1] = list[i];
      }
    } else {
      outputList = list;
    }

    return outputList;
  }

  protected void updateButtons() {
    for (ServoAdjustor servo : servos) {
      Button button = buttons.get(servo.getServoIndex());
      button.setText(Integer.toString(servo.getServoPosition()));
      boolean isSelected = button == lastSelected;
      if (servo.isAttached()) {
        if (isSelected) {
          attachButton.setText("Detach");
        }
        button.setBackgroundResource(isSelected ? R.drawable.btn_selected_attached
            : R.drawable.btn_unselected_attached);
      } else {
        if (isSelected) {
          attachButton.setText("Attach");
        }
        button.setBackgroundResource(isSelected ? R.drawable.btn_selected_detached
            : R.drawable.btn_unselected_detached);
      }
    }
  }

  private void addButtons() {
    buttons = new ArrayList<Button>(19);
    int center_x = poseCanvas.getWidth() / 2;
    int center_y = poseCanvas.getHeight() / 2 + 2;
    int radius = 72 * 3 - 5;
    int angles[] = { 120, 180, 240, 300, 0, 60 };
    int half_icon_size = 36;
    int servoNumber = 0;

    // Clear out any old buttons
    poseCanvas.removeAllViews();

    for (final int angle : angles) {
      for (int ring = 1; ring <= 3; ++ring) {
        final int servoIndex = servoNumber;
        servoNumber += 1;
        radius = (ring * 72) - 5;
        double radians = angle / 180.0 * Math.PI;

        double x = (center_x + Math.cos(radians) * radius) - half_icon_size;
        double y = (center_y - Math.sin(radians) * radius) - half_icon_size;

        final Button servoButton = new Button(this);
        buttons.add(servoButton);
        servoButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, 20);
        poseCanvas.addView(servoButton, new AbsoluteLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT, (int) x, (int) y));

        servoButton.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View v) {
            if (lastSelected != null) {
              ServoAdjustor servo = servos.get(lastServoIndex);
              if (servo.isAttached()) {
                lastSelected.setBackgroundResource(R.drawable.btn_unselected_attached);
              } else {
                lastSelected.setBackgroundResource(R.drawable.btn_unselected_detached);
              }
            }
            ServoAdjustor servo = servos.get(servoIndex);
            if (servo.isAttached()) {
              v.setBackgroundResource(R.drawable.btn_selected_attached);
            } else {
              v.setBackgroundResource(R.drawable.btn_selected_detached);
            }
            lastServoIndex = servoIndex;
            lastSelected = v;
            adjustmentBar.setOnSeekBarChangeListener(servo);
            adjustmentBar.setProgress(servo.getServoPosition());
            incrementButton.setOnClickListener(servo);
            decrementButton.setOnClickListener(servo);
            servo.clearTextViews();
            servo.addTextView(servoButton);
            attachButton.setOnClickListener(servo);
            if (servo.isAttached()) {
              attachButton.setText("Detach");
            } else {
              attachButton.setText("Attach");
            }
          }
        });
      }
    }
  }

  private void initializeServoAdjusters() {
    if (robotService != null) {
      Robot robot = robotService.getRobot();
      if (robot != null) {
        servos.clear();
        int numServos = robot.getNumServos();
        for (int i = 0; i < numServos; i++) {
          servos.add(new ServoAdjustor(robot.getServo(i)));
        }
      }
      if (servos.size() == 18) {
        addButtons();
      }
    }
  }

  @Override
  void onRobotConnected() {
    initializeServoAdjusters();
    refreshPoseList();
    updateButtons();
  }

  @Override
  void onRobotDisconnected() {
    initializeServoAdjusters();
    updateButtons();
  }

  @Override
  protected Dialog onCreateDialog(int id) {
    dialog = null;
    switch (id) {
    case DIALOG_SAVE_ID: {
      dialog = new Dialog(this);

      dialog.setContentView(R.layout.save_dialog);
      dialog.setTitle("Save Pose");
      final EditText filename = (EditText) dialog.findViewById(R.id.save_filename);
      Button cancel = (Button) dialog.findViewById(R.id.cancel_button);
      cancel.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          dialog.cancel();
        }
      });

      Button save = (Button) dialog.findViewById(R.id.save_button);
      save.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          savePose(filename.getText().toString());
          dialog.dismiss();
        }
      });

      dialog.setOnShowListener(new OnShowListener() {
        @Override
        public void onShow(DialogInterface dialog) {
          // Initialize the save name the the last pose we loaded if available;
          if (lastLoadedOrSavedPose != null) {
            filename.setText(lastLoadedOrSavedPose);
          }
        }
      });
    }

      break;
    case DIALOG_LOAD_ID: {
      dialog = new Dialog(this);

      dialog.setContentView(R.layout.save_dialog);
      dialog.setTitle("Load Pose");
      final EditText filename = (EditText) dialog.findViewById(R.id.save_filename);
      Button cancel = (Button) dialog.findViewById(R.id.cancel_button);
      cancel.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          dialog.cancel();
        }
      });

      Button save = (Button) dialog.findViewById(R.id.save_button);
      save.setText("Load");
      save.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          robotService.getRobot().detachAllServos();
          loadPose(filename.getText().toString());
          dismissDialog(DIALOG_LOAD_ID);
        }
      });
    }
      break;
    default:
      dialog = null;
    }
    return dialog;
  }

  protected void loadPose(String poseName) {
    Pose pose = Pose.load(this, poseName);

    // If the pose wasn't loaded but it was a calibration pose then create the
    // default calibration pose.
    if (pose == null && poseName.equals(calibrationPoseName) && robotService != null) {
      pose = new Pose();
      Robot robot = robotService.getRobot();
      int numServos = robot.getNumServos();
      for (int i = 0; i < numServos; ++i) {
        pose.setServo(i, robot.getCalibrationAngle(i));
      }
    }

    if (pose != null && !servos.isEmpty()) {
      // If this is the calibration pose then we want to reset the calibration
      // on the robot
      // so we are not applying to calibration poses, otherwise we apply the
      // calibration that is
      // saved.
      if (poseName.equals(calibrationPoseName)) {
        robotService.resetCalibration();
      } else {
        robotService.updateCalibrationFromSavedPose(this, calibrationPoseName);
      }
      // We need to recreate the buttons here because the robot may have changed out from
      // under us.
      initializeServoAdjusters();

      robotService.getRobot().detachAllServos();
      for (Integer servoNumber : pose.getServoList()) {
        int angle = pose.getPosition(servoNumber);
        ServoAdjustor servoAdjustor = servos.get(servoNumber);
        servoAdjustor.setServoPosition(angle, true);
      }

      lastLoadedOrSavedPose = poseName;
    }
  }

  /**
   * Saves the current servo settings to permanent memory with the given pose
   * name.
   *
   * @param poseName
   *          file name of the pose that will be saved
   */
  protected void savePose(String poseName) {
    Pose pose = new Pose();
    for (ServoAdjustor servoAdjustor : servos) {
      pose.setServo(servoAdjustor.getServoIndex(), servoAdjustor.getServoPosition());
    }
    pose.save(this, poseName);
    lastLoadedOrSavedPose = poseName;
    // I don't think we want to do this because when we are working on a calibration
    // pose the robot calibration has to be set to 0 so we don't apply the calibration
    // twice.
    // // If we are saving a calibration pose then we update the robot config.
    // if (poseName.equals(calibrationPoseName)) {
    // robotService.updateCalibrationFromSavedPose(this, calibrationPoseName);
    // }
    refreshPoseList();
  }

  class ServoAdjustor implements OnSeekBarChangeListener, OnClickListener {
    private final Servo servo;
    private final ArrayList<TextView> textViews = new ArrayList<TextView>();

    ServoAdjustor(Servo servo) {
      this.servo = servo;
    }

    public void attach() {
      servo.attach();
    }

    public void addTextView(TextView positionView) {
      textViews.add(positionView);
    }

    public void clearTextViews() {
      textViews.clear();
    }

    int getServoIndex() {
      return servo.getIndex();
    }

    void setServoPosition(int servoPosition, boolean send) {
      if (send) {
        servo.moveServo(servoPosition);
      } else {
        servo.setCurrentAngle(servoPosition);
      }
      String servoPositionString = Integer.toString(getServoPosition());
      for (TextView tv : textViews) {
        tv.setText(servoPositionString);
      }
    }

    int getServoPosition() {
      return servo.getCurrentAngle();
    }

    boolean isAttached() {
      return servo.isAttached();
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
      setServoPosition(progress, true);
    }

    @Override
    public void onStartTrackingTouch(SeekBar arg0) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar arg0) {
    }

    @Override
    public void onClick(View view) {
      if (view == incrementButton) {
        adjustmentBar.incrementProgressBy(1);
      } else if (view == decrementButton) {
        adjustmentBar.incrementProgressBy(-1);
      } else if (view == attachButton) {
        if (isAttached()) {
          servo.detach();
          attachButton.setText("Attach");
          lastSelected.setBackgroundResource(R.drawable.btn_selected_detached);
        } else {
          servo.attach();
          lastSelected.setBackgroundResource(R.drawable.btn_selected_attached);
          attachButton.setText("Detach");
        }
      }
    }
  }
}
